from time import sleep

from django.db import transaction
from django.utils.timezone import now
from django_redis import get_redis_connection
from redis import StrictRedis
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer

from goods.models import SKU
from orders.models import OrderInfo, OrderGoods


class CartSKUSerializer2(ModelSerializer):
    """确认订单使用的序列化器"""

    count = serializers.IntegerField(label='数量')

    class Meta:
        model = SKU
        fields = ('id', 'name', 'default_image_url', 'price', 'count')


class SaveOrderSerializer(ModelSerializer):
    """保存订单序列化器"""

    class Meta:
        model = OrderInfo
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        extra_kwargs = {
            'address': {
                'write_only': True,
                'required': True,
            },
            'pay_method': {
                'write_only': True,
                'required': True
            }
        }

    def create(self, validated_data):
        """保存订单"""

        # 获取下单用户及请求参数: address，pay_method
        user = self.context['request'].user
        address = validated_data.get('address')
        pay_method = validated_data.get('pay_method')
        # 生成订单编号 20180704174607000000001
        order_id = now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        # 订单信息表: 保存订单基本信息（新增一条订单数据）
        # print(1)
        with transaction.atomic():  # 开启一个事务 库存不足时候回滚，不产生订单
            save_id = transaction.savepoint()
            try:
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    pay_method=pay_method,  # 支付方式
                    total_count=0,  # 初始购买总数
                    total_amount=0,  # 初始购买总价
                    freight=10,  # 运费
                    # 三元表达式
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID']  # 未支付: 待付款
                    # 三元表达式：a if 判断条件 else b
                    if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                    else OrderInfo.ORDER_STATUS_ENUM['UNSEND']  # 货到付款：待发货
                )
                # print(2)
                # 从Redis中查询购物车商品
                # cart_1 = {1: 2, 2: 2, 3: 1}		hgetall
                # cart_selected_1 = {1, 2}		    smembers
                strict_redis = get_redis_connection('cart')  # type: StrictRedis
                dict_cart = strict_redis.hgetall('cart_%s' % user.id)
                list_selected = strict_redis.smembers('cart_selected_%s' % user.id)
                # 过滤出勾选的商品id和数量，保存到一个新的字典中：{1: 2, 2: 2}
                cart = {}
                for sku_id in list_selected:
                    cart[int(sku_id)] = int(dict_cart[sku_id])
                # 遍历勾选的商品id
                sku_ids = cart.keys()
                for sku_id in sku_ids:
                    while True:  # 使用乐观锁解决并发问题
                        # 查询sku对象
                        sku = SKU.objects.get(id=sku_id)
                        # 判断库存
                        origin_stock = sku.stock
                        origin_sales = sku.sales
                        sku_count = cart[sku.id]  # 购买数量
                        if sku_count > sku.stock:
                            raise serializers.ValidationError('库存不足')
                        # sleep(5)  # 模拟并发下单的问题
                        # 修改sku表： 减少库存，增加销量
                        # sku.stock -= sku_count
                        # sku.sales += sku_count
                        # sku.save()
                        # 使用乐观锁修改库存和销量
                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        count = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock,
                                                                                         sales=new_sales)
                        if count == 0:
                            continue  # 如果运行到这，说明有其他用户并发购买，可以再次循环判断判断库存
                        # 修改spu表： 修改SPU销量 sales
                        sku.goods.sales += sku_count
                        sku.goods.save()
                        # 订单商品表: 保存订单商品信息（新增多条数据）
                        # print(order)
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price,
                        )
                        # 累加订单商品总数量和总金额
                        order.total_count += sku_count
                        order.total_amount += (sku_count * sku.price)
                        break # 表示一个订单商品保存成功, 需要跳出while死循环， 继续保存下一个订单商品
                # 修改表：修改订单总数量和总金额
                order.total_amount += order.freight  # 实付金额 = 商品总金额 + 运费
                order.save()
                # 清除购物车中已结算的商品:
                #  hdel: {1: 2, 2: 2, 3: 1}   -->   {3: 1}
                strict_redis.hdel('cart_%s' % user.id, *list_selected)
                strict_redis.srem('cart_selected_%s' % user.id, *list_selected)

            except Exception as e:
                transaction.savepoint_rollback(save_id)  # 如果产生异常，进行回滚到指定的save_id
                print('创建订单失败:', e)
                raise e
            transaction.savepoint_commit(save_id)  # 提交事务
            # 返回新创建的订单对象 order
            return order




